﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Devart.Data.Oracle;
using System.Data;
using System.Xml;
using System.Reflection;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Data.Common;



namespace DbSharp.Framework.Driver.FieldPersistence
{

    public class OracleFieldPersistence2 : IFieldPersistence
    {

        #region IFieldPersistence<OracleDbType> members

        public List<int> ListSupportedScalarDataType
        {
            get
            {
                var list = new List<int>();
                list.Add((int)OracleDbType.Char);
                list.Add((int)OracleDbType.VarChar);
                list.Add((int)OracleDbType.NChar);
                list.Add((int)OracleDbType.NVarChar);
                list.Add((int)OracleDbType.Number);
                list.Add((int)OracleDbType.Date);
                list.Add((int)OracleDbType.TimeStamp);
                list.Add((int)OracleDbType.TimeStampLTZ);
                list.Add((int)OracleDbType.TimeStampTZ);
                list.Add((int)OracleDbType.IntervalDS);
                list.Add((int)OracleDbType.IntervalYM);
                list.Add((int)OracleDbType.Integer);
                list.Add((int)OracleDbType.Float);
                list.Add((int)OracleDbType.Double);
                list.Add((int)OracleDbType.RowId);
                return list;
            }
        }

        public List<int> ListSupportedRecordsetDataType
        {
            get
            {
                var list = new List<int>();

                list.Add((int)OracleDbType.Cursor);
                return list;
            }
        }

        public string ConvertToString(object value, int valueType)
        {
            var valueType1 = (OracleDbType)valueType;
            var exceptionFmt = "{0} type is not supported.";
            var exceptionMsg = string.Format(exceptionFmt, valueType1.ToString());
            if (!ListSupportedScalarDataType.Contains(valueType))
            {
                throw new NotSupportedException(exceptionMsg);
            }

            string result;
            switch (valueType1)
            {
                case OracleDbType.Char:
                case OracleDbType.VarChar:
                case OracleDbType.NChar:
                case OracleDbType.NVarChar:
                case OracleDbType.Number:
                case OracleDbType.Date:
                case OracleDbType.TimeStamp:
                case OracleDbType.TimeStampLTZ:
                case OracleDbType.TimeStampTZ:
                case OracleDbType.IntervalDS:
                case OracleDbType.IntervalYM:
                case OracleDbType.Integer:
                case OracleDbType.Float:
                case OracleDbType.Double:
                case OracleDbType.RowId:
                    result = FieldPersistenceHelper.SafeConvertToString(value);
                    break;
                default:
                    throw new NotSupportedException(exceptionMsg);
            }
            return result;
        }






        public decimal RestoreStringToDecimal(string valueStr, int valueType)
        {
            var valueType1 = (OracleDbType)valueType;
            decimal result;
            switch (valueType1)
            {
                case OracleDbType.Number:
                    var number = OracleNumber.Parse(valueStr);
                    result = OracleNumber.ToDecimal(number);
                    break;
                case OracleDbType.IntervalYM:
                case OracleDbType.Integer:
                case OracleDbType.Float:
                case OracleDbType.Double:
                    result = Convert.ToDecimal(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }

        public Devart.Data.Oracle.OracleNumber RestoreStringToLargeDecimal(string valueStr, int valueType)
        {
            var valueType1 = (OracleDbType)valueType;
            Devart.Data.Oracle.OracleNumber result;
            switch (valueType1)
            {
                case OracleDbType.Number:
                case OracleDbType.IntervalYM:
                case OracleDbType.Integer:
                case OracleDbType.Float:
                case OracleDbType.Double:
                    result = OracleNumber.Parse(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }


        public int GetSpecificDbType(DbDataReader dataReader, int columnIndex)
        {
            //class: Devart.Data.Oracle.OracleDataReader
            //method: public OracleType GetObjectType(int i)

            //  //******GetObjectType method does not work in current devart ado.net, at least 5.70.170.0 and below
            //var method = dataReader.GetType().GetMethod("GetObjectType");
            //var parameters = new object[] { columnIndex };
            //var type= (Devart.Data.Oracle.OracleType)method.Invoke(dataReader, parameters);
            //return (int)type.DbType;

            int providerDbType = -1;

            var reader = (Devart.Data.Oracle.OracleDataReader)dataReader;
            var typeName = reader.GetDataTypeName(columnIndex);

            //Devart.Data.Oracle.OracleDataTypes.xml contains name and enum index pair of OracleDbType
            var stream = Assembly.GetAssembly(dataReader.GetType()).GetManifestResourceStream("Devart.Data.Oracle.OracleDataTypes.xml");
            var xmlReader = new XmlTextReader(stream);
            var xmlDocument = XDocument.Load(xmlReader);
            string xPath = "/NewDataSet/DataTypes/TypeName";
            var typeNameElements = xmlDocument.XPathSelectElements(xPath);
            XElement typeNameElement = null;
            foreach (var item in typeNameElements)
            {
                string name2 = item.Value.Trim();
                if (string.Equals(typeName, name2, StringComparison.Ordinal))
                {
                    typeNameElement = item;
                    break;
                }
            }
            if (typeNameElement != null)
            {
                XElement providerDbTypeElement = typeNameElement.Parent.Element("ProviderDbType");
                providerDbType = int.Parse(providerDbTypeElement.Value.Trim());
            }
            return providerDbType;
        }




        public int GetSpecificDbType(DbParameter dbParam)
        {
            var typ = ((OracleParameter)dbParam).OracleDbType;
            return (int)typ;
        }


        public int NumberTypeWhenParseLiteral
        {
            get { return (int)(OracleDbType.Number); }
        }


       

        public bool IsStringType(int valueType)
        {
            bool result = false;
            var valueType1 = (OracleDbType)valueType;
            switch (valueType1)
            {
                case OracleDbType.Char:
                case OracleDbType.VarChar:
                case OracleDbType.NChar:
                case OracleDbType.NVarChar: 
                    result = true;
                    break;
                default:
                    result = false;
                    break;
            }

            return result;
        }
        #endregion
    }



}
